CSRF(跨站请求伪造)
用户已登录站点 A,浏览器会自动带上 A 的 Cookie。攻击者诱导用户访问恶意站点 B,B 中的表单或脚本向 A 发起请求;只要 Cookie 随请求发送且 A 未校验「本次操作是否用户本意」,就可能完成转账、改密等。
说明
以下代码仅供理解原理与本地实验,请勿用于未授权系统。
攻击示意:跨站表单 POST
恶意页面(简化):
html
<!-- https://evil.example/csrf.html -->
<body>
<form id="f" action="https://bank.example/api/transfer" method="POST">
<input name="to" value="attacker" />
<input name="amount" value="9999" />
</form>
<script>document.getElementById('f').submit();</script>
</body>若 bank.example 仅依赖 Cookie 会话且接口接受简单 POST,用户在仍登录状态下打开该页即可能触发转账。
GET 场景同理: <img src="https://bank.example/api/delete?id=1"> 若服务端把敏感操作用 GET 实现且仅靠 Cookie,危害更大。
防御一:CSRF Token(前后端配合)
后端:为会话签发随机 Token,嵌入页面或接口响应。
前端:在同源 AJAX / fetch 中附带 Token。
html
<meta name="csrf-token" content="__SERVER_RENDERED_TOKEN__" />js
const token = document.querySelector('meta[name="csrf-token"]')?.content;
await fetch('/api/transfer', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token,
},
body: JSON.stringify({ to: 'bob', amount: 100 }),
});原理:跨域站点 B 读不到 A 页面里的 Token(同源策略),表单伪造无法附带正确 Token。
防御二:SameSite Cookie
服务端设置 Cookie:
http
Set-Cookie: sid=...; HttpOnly; Secure; SameSite=LaxStrict:完全禁止跨站携带(最安全,可能影响少数跳转登录场景)。Lax(常用默认值):跨站 顶级导航 GET 仍可带 Cookie,但对多数 CSRF POST 场景有帮助。- 敏感操作建议仍配合 Token / 二次验证。
防御三:校验 Origin / Referer
服务端拒绝 Origin 不在白名单内的状态变更请求(注意浏览器在某些场景不发送 Referer)。
防御四:敏感操作二次确认
转账、改绑手机等要求短信验证码、密码再输入;攻击者无法替你完成第二步。
小结
| 手段 | 作用 |
|---|---|
| CSRF Token | 跨站页面拿不到 Token,请求被拒 |
| SameSite Cookie | 限制跨站携带 Cookie |
| Origin 校验 | 拒绝非法来源请求 |
| 二次验证 | 即使 Cookie 被带也无法单独完成敏感操作 |
